package com.lingchou.cloud.process.client.controller;

import cn.hutool.core.util.StrUtil;
import com.lc.common.bean.response.Result;
import com.lingchou.cloud.process.api.controller.ProcessQueryApi;
import com.lingchou.cloud.process.api.dto.result.AllTaskDTO;
import com.lingchou.cloud.process.api.dto.result.ProcessInstanceDTO;
import com.lingchou.cloud.process.api.dto.result.TaskDTO;
import com.lingchou.cloud.process.api.dto.result.base.ProcessTaskRecordVo;
import com.lingchou.cloud.process.api.dto.result.base.TaskRecordInfo;
import com.lingchou.cloud.process.api.enums.CommentTypeEnum;
import com.lingchou.cloud.process.client.dao.DefinitionSupplementaryDao;
import com.lingchou.cloud.process.client.entity.DefinitionSupplementary;
import com.lingchou.cloud.process.client.service.FlowAbleCommentService;
import com.lingchou.cloud.process.client.service.ProcessCheckService;
import com.lingchou.cloud.process.client.service.ProcessUtilService;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceQuery;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.constraints.Size;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@Validated
public class ProcessQueryApiImpl implements ProcessQueryApi {
    private final RuntimeService runtimeService;
    private final TaskService taskService;
    private final ProcessUtilService processUtilService;
    private final ProcessCheckService processCheckService;
    private final FlowAbleCommentService flowAbleCommentService;
    private final RepositoryService repositoryService;
    private final HistoryService historyService;
    private final DefinitionSupplementaryDao definitionSupplementaryDao;

    public ProcessQueryApiImpl(RuntimeService runtimeService, TaskService taskService, ProcessUtilService processUtilService, ProcessCheckService processCheckService, FlowAbleCommentService flowAbleCommentService, RepositoryService repositoryService, HistoryService historyService, DefinitionSupplementaryDao definitionSupplementaryDao) {
        this.runtimeService = runtimeService;
        this.taskService = taskService;
        this.processUtilService = processUtilService;
        this.processCheckService = processCheckService;
        this.flowAbleCommentService = flowAbleCommentService;
        this.repositoryService = repositoryService;
        this.historyService = historyService;
        this.definitionSupplementaryDao = definitionSupplementaryDao;
    }

    @Override
    public Result<List<ProcessInstanceDTO>> instanceList(String userId, String businessKey) {
        ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery();
        if (!StrUtil.isEmpty(businessKey)) {
            query.processInstanceBusinessKey(businessKey);
        }
        if (!StrUtil.isEmpty(userId)) {
            query.startedBy(userId);
        }
        List<ProcessInstance> processInstanceList = query.list();
        return Result.success(processUtilService.buildProcessInstanceList(processInstanceList));
    }

    @Override
    public Result<List<TaskDTO>> taskList(String userId, String businessKey) {
        List<Task> tasks = taskListForUserId(userId, businessKey);
        return Result.success(processUtilService.buildTaskDTOList(tasks));
    }

    private List<Task> taskListForUserId(String userId, String businessKey) {
        TaskQuery query = taskService.createTaskQuery();
        return query.taskCandidateOrAssigned(userId)
                .processInstanceBusinessKey(businessKey)
                .active()
                .orderByTaskCreateTime()
                .desc()
                .list();
    }

    @Override
    public Result<List<TaskDTO>> taskListByKeyLike(String userId, String businessKey, String key) {
        TaskQuery query = taskService.createTaskQuery();
        List<Task> tasks = query.taskCandidateOrAssigned(userId)
                .processInstanceBusinessKey(businessKey)
                .taskDefinitionKeyLike("%" + key + "%")
                .active()
                .orderByTaskCreateTime()
                .desc()
                .list();
        return Result.success(processUtilService.buildTaskDTOList(tasks));
    }

    @Override
    public Result<List<TaskDTO>> taskListForUsers(List<String> userIds, String businessKey) {
        List<Task> tasks = new ArrayList<>();
        userIds.forEach(userId -> tasks.addAll(taskListForUserId(userId, businessKey)));
        return Result.success(processUtilService.buildTaskDTOList(tasks));
    }

    @Override
    public Result<List<TaskDTO>> taskNow(String instanceId) {
        if (processCheckService.checkProcessInstanceInvalid(instanceId, true)) {
            return Result.failure("流程实例不存在或已完成");
        }
        TaskQuery query = taskService.createTaskQuery();
        List<Task> tasks = query.processInstanceId(instanceId).list();
        return Result.success(processUtilService.buildTaskDTOList(tasks));
    }

    @Override
    public Result<String> getVarJsonByTask(String taskId) {
        String taskDefinitionId = processUtilService.getTaskDefinitionKey(taskId);
        if (taskDefinitionId == null) {
            return Result.failure("活动ID查询失败");
        }
        DefinitionSupplementary definitionSupplementary = definitionSupplementaryDao.findByTaskDefId(taskDefinitionId);
        return definitionSupplementary == null ? Result.success(null) : Result.success(definitionSupplementary.getSelectionRange());
    }

    @Override
    public Result<List<AllTaskDTO>> getAllTask(String processInstanceId) {
        if (processCheckService.checkProcessInstanceInvalid(processInstanceId, true) &&
                processCheckService.checkProcessInstanceInvalid(processInstanceId, false)) {
            return Result.failure("流程实例不存在");
        }
        //历史任务
        List<HistoricTaskInstance> historicTaskInstances = getHisTask(processInstanceId);
        List<AllTaskDTO> hisTasks = processUtilService.buildHistoricTaskInstanceToAllTaskDTOs(historicTaskInstances);
        //未执行任务
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        List<UserTask> userTasks = getUserTask(historicProcessInstance.getProcessDefinitionId());
        List<AllTaskDTO> userTaskDTOs = processUtilService.buildUserTasksToAllTaskDTO(userTasks);
        //获取历史与当前的任务定义ID 在所有task列表中去除 合并返回
        Set<String> taskDefIds = hisTasks.stream().map(AllTaskDTO::getTaskDefId).collect(Collectors.toSet());
        userTaskDTOs = userTaskDTOs.stream().filter(u -> !taskDefIds.contains(u.getTaskDefId())).collect(Collectors.toList());
        //合并
        hisTasks.addAll(userTaskDTOs);
        return Result.success(hisTasks);
    }

    @Override
    public Result<List<ProcessTaskRecordVo>> getRecord(String processInstanceId) {
        return Result.success(queryRecordInfo(processInstanceId));
    }

    private List<ProcessTaskRecordVo> queryRecordInfo(String processInstanceId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (historicProcessInstance == null) {
            return Collections.emptyList();
        }
        //任务定义
        List<UserTask> userTasks = getUserTask(historicProcessInstance.getProcessDefinitionId());
        List<ProcessTaskRecordVo> processTaskRecordVos = processUtilService.buildUserTasksToProcessTaskRecordVos(userTasks, processInstanceId);
        //历史任务记录
        List<TaskRecordInfo> records = flowAbleCommentService.getRecord(processInstanceId);
        processTaskRecordVos.forEach(processTaskRecordVo -> {
            //拿出属于同一taskDefId的任务
            List<TaskRecordInfo> taskRecordInfos = records.stream()
                    .filter(record -> {
                        if (!record.getTaskDefId().equals(processTaskRecordVo.getTaskDefId())) {
                            return false;
                        }
                        //删除原因不为空是时，且包含"Change parent activity to"，没有评论时间 视为 拒绝的任务的兄弟任务
                        return record.getDeleteReason() == null
                                || !record.getDeleteReason().contains("Change parent activity to") || record.getSignTime() != null;
                    }).collect(Collectors.toList());
            processTaskRecordVo.setRecordInfoList(taskRecordInfos);
            List<String> types = taskRecordInfos.stream().map(TaskRecordInfo::getSignType).collect(Collectors.toList());
            if (types.isEmpty()) {
                processTaskRecordVo.setStatus(CommentTypeEnum.NOT_APPROVED.name());
                return;
            }
            //默认审批通过
            processTaskRecordVo.setStatus(CommentTypeEnum.APPROVAL.name());
            //包含正在审批则是审批中
            if (types.contains(CommentTypeEnum.IN_REVIEW.name())) {
                processTaskRecordVo.setStatus(CommentTypeEnum.IN_REVIEW.name());
                return;
            }
            //包含审批超时则是审批超时
            if (types.contains(CommentTypeEnum.TIME_OUT.name())) {
                processTaskRecordVo.setStatus(CommentTypeEnum.TIME_OUT.name());
                return;
            }
            //包含审批拒绝则是审批拒绝
            if (types.contains(CommentTypeEnum.TURN_DOWN.name())) {
                processTaskRecordVo.setStatus(CommentTypeEnum.TURN_DOWN.name());
            }
            //包含审批撤销则是审批撤销
            if (types.contains(CommentTypeEnum.REVOKE.name())) {
                processTaskRecordVo.setStatus(CommentTypeEnum.REVOKE.name());
            }
        });
        return processTaskRecordVos;
    }


    @Override
    public Result<Map<String, List<ProcessTaskRecordVo>>> queryRecordBatch(@Size(max = 100, message = "介于性能问题，查询量应小于100") List<String> processInstanceIds) {
        Map<String, List<ProcessTaskRecordVo>> resultMap = new HashMap<>();
        processInstanceIds.forEach(p -> resultMap.put(p, queryRecordInfo(p)));
        return Result.success(resultMap);
    }

    @Override
    public Result<String> getLastApprover(String processInstanceId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (historicProcessInstance == null) {
            return Result.failure("流程不存在");
        }
        //历史任务记录
        List<TaskRecordInfo> records = flowAbleCommentService.getRecord(processInstanceId);
        TaskRecordInfo taskRecordInfo = records.stream().min(Comparator.comparing(TaskRecordInfo::getEndTime)).orElse(null);
        if (taskRecordInfo == null) {
            return Result.failure("任务列表不存在");
        }
        return Result.success(taskRecordInfo.getAssignee());
    }

    /**
     * 获取历史任务与当前任务
     *
     * @param instanceId 流程实例ID
     * @return 任务列表
     */
    private List<HistoricTaskInstance> getHisTask(String instanceId) {
        if (StrUtil.isBlank(instanceId)) {
            return new ArrayList<>();
        }
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery();
        return query.processInstanceId(instanceId).orderByTaskCreateTime().asc().list();
    }

    /**
     * 获取流程定义的所有UserTask
     *
     * @param defId 流程定义ID
     * @return UserTask 节点集合
     */
    private List<UserTask> getUserTask(String defId) {
        Process process = repositoryService.getBpmnModel(defId).getMainProcess();
        return process.findFlowElementsOfType(UserTask.class);
    }

    @Override
    public Result<Boolean> judgeTheLastNode(String taskId) {
        //1.查看当前任务是存在
        TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
        if (taskEntity == null) {
            return Result.failure("任务不存在");
        }
        // 获取流程配置
        List<UserTask> userTasks = getUserTask(taskEntity.getProcessDefinitionId());
        //最后一个节点
        UserTask lastTask = userTasks.get(userTasks.size() - 1);
        //匹配最后一个节点的ID和任务的defId 相同则返回true
        return lastTask.getId().equals(taskEntity.getTaskDefinitionKey())
                ? Result.success(Boolean.TRUE)
                : Result.success(Boolean.FALSE);
    }

    @Override
    public Result<String> queryNodeType(String taskId) {
        //1.查看当前任务是存在
        TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
        if (taskEntity == null) {
            return Result.failure("任务不存在");
        }
        DefinitionSupplementary definitionSupplementary = definitionSupplementaryDao.findByTaskDefId(taskEntity.getTaskDefinitionKey());
        //根据执行器获取流程实例ID，与类型参数，作为参数查询变量值
        HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(taskEntity.getProcessInstanceId())
                .variableName(definitionSupplementary.getNodeTypeParamName()).singleResult();
        return Result.success(historicVariableInstance.getValue().toString());
    }
}
